package com.dcy.system.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.Header;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dcy.common.base.service.DcyBaseService;
import com.dcy.common.constant.RedisConstant;
import com.dcy.common.enums.AdminApiErrorCode;
import com.dcy.common.enums.FlowTypeEnum;
import com.dcy.common.exception.BusinessException;
import com.dcy.common.model.LoginAccount;
import com.dcy.common.model.PageModel;
import com.dcy.common.model.PageResult;
import com.dcy.common.model.flowable.ActUserModel;
import com.dcy.common.service.ActUserService;
import com.dcy.common.utils.AdminLoginUtil;
import com.dcy.common.utils.ViewMapper;
import com.dcy.system.convert.RoleConvert;
import com.dcy.system.convert.UserInfoConvert;
import com.dcy.system.dao.*;
import com.dcy.system.enums.UserInfoSexEnum;
import com.dcy.system.enums.UserInfoStatusEnum;
import com.dcy.system.model.*;
import com.dcy.system.vo.in.*;
import com.dcy.system.vo.out.*;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author dcy
 * @since 2020-08-19
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class UserInfoService extends DcyBaseService implements ActUserService {

    private static final BCryptPasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
    private final UserInfoDao userInfoDao;
    private final UserPostDao userPostDao;
    private final UserRoleDao usrRoleDao;
    private final DeptDao deptDao;
    private final RoleDao roleDao;
    private final AuthService authService;
    private final UserInfoConvert userInfoConvert = UserInfoConvert.INSTANCE;
    private final RoleConvert roleConvert = RoleConvert.INSTANCE;


    /**
     * 登录
     *
     * @param loginInVO
     * @return
     */
    public String login(LoginInVO loginInVO) {
        UserInfo userInfo = userInfoDao.getUserInfoByUsername(loginInVO.getUsername());
        if (userInfo == null) {
            throw new BusinessException(AdminApiErrorCode.USER_PASSWORD_ERROR);
        }
        if (UserInfoStatusEnum.DISABLE.getCode().equals(userInfo.getUserStatus())) {
            throw new BusinessException(AdminApiErrorCode.USER_LOCKED_ERROR);
        }
        if (!PASSWORD_ENCODER.matches(loginInVO.getPassword(), userInfo.getPassword())) {
            throw new BusinessException(AdminApiErrorCode.USER_PASSWORD_ERROR);
        }
        LoginAccount loginAccount = userInfoConvert.toLoginOutput(userInfo);
        // 设置权限
        loginAccount.setResources(authService.getAuthRoleAndResourceByUserId(userInfo.getId()));
        if (StrUtil.isNotBlank(userInfo.getDeptId())) {
            loginAccount.setDeptName(deptDao.getById(userInfo.getDeptId()).getName());
        }
        List<Role> authRoleListByUserId = roleDao.selectRoleListByUserId(userInfo.getId());
        if (CollUtil.isNotEmpty(authRoleListByUserId)) {
            loginAccount.setRoleName(CollUtil.join(authRoleListByUserId.stream().map(Role::getRoleName).collect(Collectors.toList()), ","));
        }
        // 设置数据权限
        final Boolean allDataScopeFlag = authService.getAllDataScopeFlag(userInfo.getId());
        loginAccount.setAllDataScopeFlag(allDataScopeFlag);
        if (!allDataScopeFlag) {
            loginAccount.setDataScopes(authService.getDataScopeListByUserId(userInfo.getId()));
        }
        return AdminLoginUtil.login(loginAccount);
    }

    /**
     * 获取用户信息
     *
     * @return
     */
    public LoginAccount getUserInfo() {
        return AdminLoginUtil.getUserInfo();
    }

    /**
     * 根据用户名获取用户信息
     *
     * @param username
     * @return
     */
    public UserInfoListOutVO getUserInfoByUsername(String username) {
        return userInfoConvert.toOut(userInfoDao.getUserInfoByUsername(username));
    }

    /**
     * 根据用户id 查询已授权角色列表
     *
     * @param userId
     * @return
     */
    public List<RoleListOutVO> getAuthRoleListByUserId(String userId) {
        return roleConvert.toOutList(roleDao.selectRoleListByUserId(userId));
    }

    /**
     * 保存授权角色
     *
     * @param userInfoRoleOutVO
     * @return
     */
    @CacheEvict(value = {RedisConstant.REDIS_USER_ROLE}, allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveAuthRole(UserInfoRoleOutVO userInfoRoleOutVO) {
        boolean success = false;
        if (StrUtil.isNotBlank(userInfoRoleOutVO.getUserId()) && userInfoRoleOutVO.getRoleIds() != null) {
            // 删除关联表
            usrRoleDao.remove(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, userInfoRoleOutVO.getUserId()));
            // 添加关联表
            usrRoleDao.saveBatch(userInfoRoleOutVO.getRoleIds().stream().map(roleId -> new UserRole().setUserId(userInfoRoleOutVO.getUserId()).setRoleId(roleId)).collect(Collectors.toList()));
            success = true;
        }
        return success;
    }

    /**
     * 自定义sql分页查询
     *
     * @param userInfoSearchInVO
     * @param pageModel
     * @return
     */
    public PageResult<UserInfoListOutVO> pageUserList(UserInfoSearchInVO userInfoSearchInVO, PageModel pageModel) {
        return toPageResult(userInfoDao.pageListByEntity(userInfoConvert.toUserInfo(userInfoSearchInVO), pageModel).convert(userInfoConvert::toOut));
    }

    /**
     * 用户分页查询-lamda方式
     *
     * @param userInfoSearchInVO
     * @param pageModel
     * @return
     */
    public PageResult<UserInfoListOutVO> pageList(UserInfoSearchInVO userInfoSearchInVO, PageModel pageModel) {
        final IPage<UserInfoListOutVO> userInfoIPage = userInfoDao.pageListByEntityLam(userInfoConvert.toUserInfo(userInfoSearchInVO), pageModel).convert(userInfoConvert::toOut);
        return toPageResult(userInfoIPage, ViewMapper.batchMapView(userInfoIPage.getRecords(), deptDao::getMapDept, UserInfoListOutVO::getDeptId, (userInfoListOutVO, dept) -> userInfoListOutVO.setDeptName(dept.getName())));
    }

    /**
     * 保存用户和岗位
     *
     * @param userInfoCreateInVO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean save(UserInfoCreateInVO userInfoCreateInVO) {
        boolean success = false;
        UserInfo userInfo = userInfoConvert.toUserInfo(userInfoCreateInVO);
        userInfo.setPassword(PASSWORD_ENCODER.encode(userInfoCreateInVO.getPassword()));
        if (userInfoDao.save(userInfo)) {
            userPostDao.remove(Wrappers.<UserPost>lambdaQuery().eq(UserPost::getUserId, userInfo.getId()));
            userPostDao.saveBatch(userInfoCreateInVO.getPostIds().stream().map(postId -> new UserPost().setUserId(userInfo.getId()).setPostId(postId)).collect(Collectors.toList()));
            success = true;
        }
        return success;
    }


    /**
     * 修改用户和岗位
     *
     * @param userInfoUpdateInVO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean update(UserInfoUpdateInVO userInfoUpdateInVO) {
        boolean success = false;
        UserInfo userInfo = userInfoConvert.toUserInfo(userInfoUpdateInVO);
        if (userInfoDao.updateById(userInfo)) {
            userPostDao.remove(Wrappers.<UserPost>lambdaQuery().eq(UserPost::getUserId, userInfo.getId()));
            userPostDao.saveBatch(userInfoUpdateInVO.getPostIds().stream().map(postId -> new UserPost().setUserId(userInfo.getId()).setPostId(postId)).collect(Collectors.toList()));
            success = true;
        }
        return success;
    }


    /**
     * 重置密码
     *
     * @param userInfoResetPassInVO
     * @return
     */
    public boolean resetPassword(UserInfoResetPassInVO userInfoResetPassInVO) {
        UserInfo userInfo = userInfoConvert.toUserInfo(userInfoResetPassInVO);
        userInfo.setPassword(PASSWORD_ENCODER.encode(userInfoResetPassInVO.getPassword()));
        return userInfoDao.updateById(userInfo);
    }

    /**
     * 修改基本信息
     *
     * @param userInfoUpdateInfoInVO
     * @return
     */
    public boolean updateInfo(UserInfoUpdateInfoInVO userInfoUpdateInfoInVO) {
        return userInfoDao.updateById(userInfoConvert.toUserInfo(userInfoUpdateInfoInVO));
    }

    public boolean updatePass(UserUpdatePassInVO userUpdatePassInVO) {
        UserInfo userInfo = userInfoDao.getById(userUpdatePassInVO.getUserId());
        if (!PASSWORD_ENCODER.matches(userUpdatePassInVO.getCurrentPass(), userInfo.getPassword())) {
            throw new BusinessException(AdminApiErrorCode.USER_UPDATE_PASS_ERROR);
        }
        if (!userUpdatePassInVO.getNewPass().equalsIgnoreCase(userUpdatePassInVO.getConfPass())) {
            throw new BusinessException(AdminApiErrorCode.USER_UPDATE_PASS2_ERROR);
        }
        UserInfo updatePassUser = new UserInfo();
        updatePassUser.setId(userUpdatePassInVO.getUserId());
        updatePassUser.setPassword(PASSWORD_ENCODER.encode(userUpdatePassInVO.getNewPass()));
        return userInfoDao.updateById(updatePassUser);
    }

    /**
     * 根据用户id 查询岗位id
     *
     * @param userId
     * @return
     */
    public List<String> getPostListByUserId(String userId) {
        return userPostDao.getPostIdListByUserId(userId);
    }

    /**
     * 删除
     *
     * @param id
     * @return
     */
    public Boolean delete(String id) {
        return userInfoDao.removeById(id);
    }

    /**
     * 批量删除
     *
     * @param idList
     * @return
     */
    public Boolean deleteBatch(List<String> idList) {
        return userInfoDao.removeBatchByIds(idList);
    }

    @Override
    public ActUserModel getActUserByUserId(String userId) {
        if (StrUtil.isBlank(userId)) {
            return null;
        }
        final UserInfo userInfo = userInfoDao.getById(userId);
        if (userInfo == null) {
            return null;
        }
        return new ActUserModel().setUserId(userInfo.getId()).setUsername(userInfo.getUsername()).setNickName(userInfo.getNickName());
    }

    @Override
    public String getNickNameByUserId(String userId) {
        if (StrUtil.isBlank(userId)) {
            return null;
        }
        final UserInfo userInfo = userInfoDao.getById(userId);
        if (userInfo == null) {
            return null;
        }
        return userInfo.getNickName();
    }

    @Override
    public List<String> getRoleIdsAndDeptId(String userId) {
        final UserInfo userInfo = userInfoDao.getById(userId);
        final List<String> roleIds = usrRoleDao.getRoleIdListByUserId(userId);
        List<String> list = new ArrayList<>();
        if (userInfo != null && StrUtil.isNotBlank(userInfo.getDeptId())) {
            list.add(userInfo.getDeptId());
        }
        if (CollUtil.isNotEmpty(roleIds)) {
            list.addAll(roleIds);
        }
        return list;
    }

    /**
     * 获取活动节点信息
     *
     * @param flowType
     * @param search
     * @return
     */
    public List<OptionDataListOutVO> getOptionDataByFlowType(String flowType, String search) {
        List<OptionDataListOutVO> list = new ArrayList<>();
        FlowTypeEnum flowTypeEnum = FlowTypeEnum.getByCode(flowType);
        if (flowTypeEnum != null) {
            switch (flowTypeEnum) {
                case USER:
                    userInfoDao.list(Wrappers.<UserInfo>lambdaQuery().like(StrUtil.isNotBlank(search), UserInfo::getNickName, search)).forEach(userInfo -> list.add(new OptionDataListOutVO(userInfo.getId(), userInfo.getNickName())));
                    break;
                case ROLE:
                    roleDao.list(Wrappers.<Role>lambdaQuery().like(StrUtil.isNotBlank(search), Role::getRoleName, search)).forEach(role -> list.add(new OptionDataListOutVO(role.getId(), role.getRoleName())));
                    break;
                case DEPT:
                    deptDao.list(Wrappers.<Dept>lambdaQuery().like(StrUtil.isNotBlank(search), Dept::getName, search)).forEach(dept -> list.add(new OptionDataListOutVO(dept.getId(), dept.getName())));
                    break;
                default:
            }
        }
        return list;
    }

    /**
     * 获取导出excel数据
     *
     * @return
     */
    @SneakyThrows
    public void exportExcel(HttpServletResponse response) {
        String fileName = "用户列表.xlsx";
        response.setHeader(Header.CONTENT_DISPOSITION.getValue(), StrUtil.format("attachment;filename=\"{}\"",
                URLUtil.encode(fileName)));
        response.setContentType(ExcelUtil.XLSX_CONTENT_TYPE);
        final List<UserInfoListExcelOutVO> userInfoListExcelOutVOS = userInfoDao.list().stream().map(userInfo -> CompletableFuture.supplyAsync(() -> {
                    final UserInfoListExcelOutVO userInfoListExcelOutVO = userInfoConvert.toExcel(userInfo);
                    userInfoListExcelOutVO.setSexName(UserInfoSexEnum.getNameByCode(userInfo.getSex()));
                    userInfoListExcelOutVO.setUserStatusName(UserInfoStatusEnum.getNameByCode(userInfo.getUserStatus()));
                    return userInfoListExcelOutVO;
                })).collect(Collectors.toList())
                .stream().map(CompletableFuture::join)
                .collect(Collectors.toList());
        EasyExcel.write(response.getOutputStream(), UserInfoListExcelOutVO.class)
                .sheet()
                .doWrite(userInfoListExcelOutVOS);
    }
}
